<template>
  <div class="pdf-preview">
    <div class="pdf-wrap">
      <!-- PDF预览区域 -->
      <VuePdfEmbed
        v-if="!loading && state.source"
        ref="pdfRef"
        :source="state.source"
        :style="scale"
        class="vue-pdf-embed"
        :page="state.pageNum"
        @loaded="handleDocument"
      />

      <!-- 加载状态 -->
      <div v-else-if="loading" class="loading">
        <div class="loading-content">
          <div class="loading-text">正在下载文件...</div>
          <div class="progress-bar">
            <div class="progress-fill" :style="{ width: downloadProgress + '%' }"></div>
          </div>
          <div class="progress-text">{{ downloadProgress }}%</div>
        </div>
      </div>

      <!-- 无文件状态 -->
      <div v-else-if="!state.source" class="no-file">
        <div>暂无PDF文件</div>
        <el-button v-if="props.cosSignedUrl" type="primary" @click="manualDownload"> 点击下载并预览 </el-button>
      </div>
    </div>

    <!-- 工具栏 -->
    <div class="page-tool">
      <div class="page-tool-item" @click="lastPage">上一页</div>
      <div class="page-tool-item" @click="nextPage">下一页</div>
      <div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div>
      <div class="page-tool-item" @click="pageZoomOut">放大</div>
      <div class="page-tool-item" @click="pageZoomIn">缩小</div>
      <div class="page-tool-item" @click="downloadPdf">下载</div>
      <div class="page-tool-item" @click="printPdf">打印</div>
    </div>
  </div>
</template>
<script setup lang="ts">
  import { reactive, computed, ref, watchEffect, onUnmounted } from 'vue'
  import VuePdfEmbed from 'vue-pdf-embed'
  import { ElMessage } from 'element-plus'

  const loading = ref(false)
  const downloadProgress = ref(0) // 下载进度
  const fileObjectUrl = ref('')
  const downloadedBlob = ref<Blob | null>(null) // 存储下载完成的完整文件

  const props = defineProps({
    pdfUrl: {
      type: String,
      required: false,
    },
    cosSignedUrl: {
      type: String, // COS预签名URL
      required: false,
    },
    pdfFile: {
      type: Object, // File 对象或 UploadFile 对象
      required: false,
    },
    fileName: {
      type: String,
      default: 'document.pdf',
    },
    chunkSize: {
      type: Number,
      default: 2 * 1024 * 1024, // 默认2MB分片
    },
    maxConcurrency: {
      type: Number,
      default: 3, // 最大并发数
    },
  })

  const state = reactive({
    source: '', // 预览pdf文件地址
    pageNum: 1, // 当前页面
    scale: 1, // 缩放比例
    numPages: 0, // 总页数
    watermark: '水印', // 水印文字
  })

  // 获取文件大小
  const getFileSize = async (url: string): Promise<number> => {
    try {
      const response = await fetch(url, { method: 'HEAD' })
      const size = parseInt(response.headers.get('content-length') || '0')
      return size
    } catch (error) {
      console.error('获取文件大小失败:', error)
      throw error
    }
  }

  // 下载分片
  const downloadChunk = async (url: string, start: number, end: number): Promise<ArrayBuffer> => {
    console.log('分片下载触发！')
    try {
      const response = await fetch(url, {
        headers: {
          Range: `bytes=${start}-${end}`,
        },
      })
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }
      return response.arrayBuffer()
    } catch (error) {
      console.error(`下载分片失败 (${start}-${end}):`, error)
      throw error
    }
  }

  // 分片下载文件
  const downloadFileInChunks = async (url: string): Promise<Blob> => {
    try {
      loading.value = true
      downloadProgress.value = 0

      // 1. 获取文件总大小
      const fileSize = await getFileSize(url)
      console.log(`文件大小: ${(fileSize / 1024 / 1024).toFixed(2)}MB`)
      // 2. 如果文件小于分片大小，直接下载
      if (fileSize <= props.chunkSize) {
        const response = await fetch(url)
        const blob = await response.blob()
        downloadProgress.value = 100
        return blob
      }
      // 3. 计算分片数量
      const totalChunks = Math.ceil(fileSize / props.chunkSize)
      console.log(`总分片数: ${totalChunks}`)
      // 4. 创建下载任务队列
      const downloadTasks: Array<() => Promise<{ index: number; data: ArrayBuffer }>> = []
      for (let i = 0; i < totalChunks; i++) {
        const start = i * props.chunkSize
        const end = Math.min(start + props.chunkSize - 1, fileSize - 1)
        downloadTasks.push(async () => {
          const data = await downloadChunk(url, start, end)
          return { index: i, data }
        })
      }

      // 5. 并发下载分片
      const results: Array<{ index: number; data: ArrayBuffer }> = []
      let completedChunks = 0

      // 分批执行下载任务，控制并发数
      for (let i = 0; i < downloadTasks.length; i += props.maxConcurrency) {
        const batch = downloadTasks.slice(i, i + props.maxConcurrency)
        const batchResults = await Promise.all(batch.map((task) => task()))

        results.push(...batchResults)
        completedChunks += batchResults.length

        // 更新进度
        downloadProgress.value = Math.floor((completedChunks / totalChunks) * 100)
      }

      // 6. 按顺序合并分片
      results.sort((a, b) => a.index - b.index)
      const mergedBuffer = new Uint8Array(fileSize)
      let offset = 0

      results.forEach((result) => {
        const chunk = new Uint8Array(result.data)
        mergedBuffer.set(chunk, offset)
        offset += chunk.length
      })

      // 7. 创建最终的Blob
      const finalBlob = new Blob([mergedBuffer], { type: 'application/pdf' })
      downloadProgress.value = 100

      console.log('分片下载完成，文件大小:', (finalBlob.size / 1024 / 1024).toFixed(2) + 'MB')
      return finalBlob
    } catch (error) {
      console.error('分片下载失败:', error)
      ElMessage.error('文件下载失败，请重试')
      throw error
    } finally {
      loading.value = false
    }
  }

  // 处理文件数据源
  const initializeSource = async () => {
    try {
      if (props.cosSignedUrl) {
        // 优先尝试直接预览，失败时采用分片下载
        console.log('尝试直接预览COS文件:', props.cosSignedUrl)

        // 先尝试直接使用URL预览（适用于已配置CORS的情况）
        try {
          state.source = props.cosSignedUrl
          loading.value = false
          console.log('直接预览模式启用')
          return
        } catch (directPreviewError) {
          console.log('直接预览失败，切换到分片下载模式')
        }

        // 如果直接预览失败，使用渐进式下载
        await progressiveDownload(props.cosSignedUrl)
      } else if (props.pdfUrl) {
        // 直接使用 URL
        state.source = props.pdfUrl
      } else if (props.pdfFile) {
        // 处理 File 对象或 UploadFile 对象
        let file = null
        if (props.pdfFile.raw) {
          // UploadFile 对象，取出 raw 属性
          file = props.pdfFile.raw
        } else if (props.pdfFile instanceof File) {
          // 直接是 File 对象
          file = props.pdfFile
        }

        if (file) {
          downloadedBlob.value = file
          // 创建临时 URL
          if (fileObjectUrl.value) {
            URL.revokeObjectURL(fileObjectUrl.value)
          }
          fileObjectUrl.value = URL.createObjectURL(file)
          state.source = fileObjectUrl.value
          console.log('创建PDF预览URL:', fileObjectUrl.value)
        }
      }
    } catch (error) {
      console.error('初始化PDF源失败:', error)
      ElMessage.error('PDF文件加载失败')
    }
  }

  // 渐进式下载（优化用户体验）
  const progressiveDownload = async (url: string) => {
    try {
      loading.value = true
      downloadProgress.value = 0

      // 先快速下载前几个分片，让用户尽快看到内容
      const fileSize = await getFileSize(url)
      console.log(`文件大小: ${(fileSize / 1024 / 1024).toFixed(2)}MB`)

      // 计算分片数量
      const totalChunks = Math.ceil(fileSize / props.chunkSize)
      console.log(`总分片数: ${totalChunks}`)

      // 优先下载前3个分片（约6MB），让用户快速看到前几页
      const priorityChunks = Math.min(3, totalChunks)
      console.log(`优先下载前 ${priorityChunks} 个分片`)

      // 1. 快速下载优先分片
      const priorityData = await downloadPriorityChunks(url, priorityChunks, fileSize)

      // 2. 创建临时PDF用于快速预览
      if (priorityData.length > 0) {
        const tempBlob = createTemporaryPdf(priorityData, fileSize)
        if (tempBlob) {
          // 先显示部分内容
          if (fileObjectUrl.value) {
            URL.revokeObjectURL(fileObjectUrl.value)
          }
          fileObjectUrl.value = URL.createObjectURL(tempBlob)
          state.source = fileObjectUrl.value
          loading.value = false
          ElMessage.success('文档预览已就绪，正在后台下载完整文件...')
        }
      }

      // 3. 后台继续下载剩余分片
      if (totalChunks > priorityChunks) {
        console.log('后台下载剩余分片...')
        backgroundDownloadRemaining(url, priorityChunks, totalChunks, fileSize, priorityData)
      } else {
        // 如果文件很小，直接完成
        const finalBlob = createFinalPdf(priorityData, fileSize)
        downloadedBlob.value = finalBlob
        downloadProgress.value = 100
      }
    } catch (error) {
      console.error('渐进式下载失败:', error)
      ElMessage.error('文件加载失败，请重试')
      loading.value = false
    }
  }

  // 下载优先分片
  const downloadPriorityChunks = async (url: string, priorityCount: number, fileSize: number) => {
    const chunks = []

    for (let i = 0; i < priorityCount; i++) {
      const start = i * props.chunkSize
      const end = Math.min(start + props.chunkSize - 1, fileSize - 1)

      try {
        const data = await downloadChunk(url, start, end)
        chunks.push({ index: i, data })
        downloadProgress.value = Math.floor(((i + 1) / priorityCount) * 50) // 前50%进度
      } catch (error) {
        console.error(`优先分片 ${i} 下载失败:`, error)
        break
      }
    }

    return chunks
  }

  // 创建临时PDF（仅用于快速预览）
  const createTemporaryPdf = (chunks: any[], totalSize: number) => {
    try {
      // 合并已下载的分片
      const downloadedSize = chunks.reduce((sum, chunk) => sum + chunk.data.byteLength, 0)
      const mergedBuffer = new Uint8Array(downloadedSize)
      let offset = 0

      chunks.forEach((chunk) => {
        const chunkData = new Uint8Array(chunk.data)
        mergedBuffer.set(chunkData, offset)
        offset += chunkData.length
      })

      // 注意：这里创建的可能是不完整的PDF，仅用于快速预览
      return new Blob([mergedBuffer], { type: 'application/pdf' })
    } catch (error) {
      console.error('创建临时PDF失败:', error)
      return null
    }
  }

  // 后台下载剩余分片
  const backgroundDownloadRemaining = async (
    url: string,
    startIndex: number,
    totalChunks: number,
    fileSize: number,
    existingChunks: any[],
  ) => {
    try {
      const remainingChunks = []

      // 下载剩余分片
      for (let i = startIndex; i < totalChunks; i++) {
        const start = i * props.chunkSize
        const end = Math.min(start + props.chunkSize - 1, fileSize - 1)

        try {
          const data = await downloadChunk(url, start, end)
          remainingChunks.push({ index: i, data })

          // 更新进度（50% - 100%）
          const remainingProgress = ((i - startIndex + 1) / (totalChunks - startIndex)) * 50
          downloadProgress.value = 50 + remainingProgress
        } catch (error) {
          console.error(`分片 ${i} 下载失败:`, error)
        }
      }

      // 合并所有分片创建完整PDF
      const allChunks = [...existingChunks, ...remainingChunks]
      allChunks.sort((a, b) => a.index - b.index)

      const finalBlob = createFinalPdf(allChunks, fileSize)
      downloadedBlob.value = finalBlob

      // 更新预览源为完整文件
      if (fileObjectUrl.value) {
        URL.revokeObjectURL(fileObjectUrl.value)
      }
      fileObjectUrl.value = URL.createObjectURL(finalBlob)
      state.source = fileObjectUrl.value

      downloadProgress.value = 100
      console.log('完整文件下载完成，文件大小:', (finalBlob.size / 1024 / 1024).toFixed(2) + 'MB')
      ElMessage.success('完整文件加载完成')
    } catch (error) {
      console.error('后台下载失败:', error)
    }
  }

  // 创建最终完整PDF
  const createFinalPdf = (chunks: any[], totalSize: number) => {
    const mergedBuffer = new Uint8Array(totalSize)
    let offset = 0

    chunks.forEach((chunk) => {
      const chunkData = new Uint8Array(chunk.data)
      mergedBuffer.set(chunkData, offset)
      offset += chunkData.length
    })

    return new Blob([mergedBuffer], { type: 'application/pdf' })
  }

  // 监听 props 变化
  watchEffect(() => {
    if (props.cosSignedUrl || props.pdfUrl || props.pdfFile) {
      initializeSource()
    }
  })
  // 监听页数变化
  watchEffect(() => {
    if (state.numPages > 0) {
      loading.value = false
    }
  })
  // 组件卸载时清理 URL
  const cleanup = () => {
    if (fileObjectUrl.value) {
      URL.revokeObjectURL(fileObjectUrl.value)
      fileObjectUrl.value = ''
    }
    downloadedBlob.value = null
  }
  const _initWatermark = () => {
    let canvas = document.createElement('canvas')
    canvas.width = 200
    canvas.height = 200
    let ctx = canvas.getContext('2d')
    ctx.rotate((-18 * Math.PI) / 180)
    ctx.font = '14px Vedana'
    ctx.fillStyle = 'rgba(200, 200, 200, .3)'
    ctx.textAlign = 'left'
    ctx.textBaseline = 'middle'
    ctx.fillText(state.watermark, 50, 50)
    return canvas
  }
  const pdfCanvasRef = ref()
  const _renderWatermark = () => {
    let ctx = pdfCanvasRef.value.getContext('2d')

    // 平铺水印
    let pattern = ctx.createPattern(_initWatermark(), 'repeat')
    ctx.rect(0, 0, 900, 900)
    ctx.fillStyle = pattern
    ctx.fill()
  }

  // 用来获取总后页数
  const handleDocument = (pdf: any) => {
    console.log(pdf)
    state.numPages = pdf._pdfInfo.numPages
    // _renderWatermark();
  }

  const scale = computed(() => `transform:scale(${state.scale})`)
  function lastPage() {
    if (state.pageNum > 1) {
      state.pageNum -= 1
    }
  }
  function nextPage() {
    if (state.pageNum < state.numPages) {
      state.pageNum += 1
    }
  }
  function pageZoomOut() {
    if (state.scale < 2) {
      state.scale += 0.1
    }
  }
  function pageZoomIn() {
    if (state.scale > 1) {
      state.scale -= 0.1
    }
  }
  // 打印
  const pdfRef = ref()
  const printPdf = () => {
    pdfRef.value.print(100, 'test', true)
  }

  // 下载
  const downloadPdf = () => {
    if (downloadedBlob.value) {
      // 使用已下载的Blob进行下载
      const a = document.createElement('a')
      const url = URL.createObjectURL(downloadedBlob.value)
      a.href = url
      a.download = props.fileName
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      URL.revokeObjectURL(url)
      ElMessage.success('文件下载完成')
    } else if (props.pdfUrl) {
      // URL 下载（兜底方案）
      const a = document.createElement('a')
      a.href = props.pdfUrl
      a.download = props.fileName
      a.click()
    } else if (fileObjectUrl.value) {
      // File 对象下载（兜底方案）
      const a = document.createElement('a')
      a.href = fileObjectUrl.value
      a.download = props.fileName
      a.click()
    } else {
      ElMessage.warning('没有可下载的文件')
    }
  }

  // 手动触发分片下载
  const manualDownload = async () => {
    if (props.cosSignedUrl && !downloadedBlob.value) {
      try {
        const blob = await downloadFileInChunks(props.cosSignedUrl)
        downloadedBlob.value = blob
        ElMessage.success('文件下载完成，可以进行下载或预览')
      } catch (error) {
        console.error('手动下载失败:', error)
      }
    }
  }

  // 清理函数
  onUnmounted(() => {
    cleanup()
  })

  // 暴露给父组件的方法和属性
  defineExpose({
    downloadedBlob,
    downloadProgress,
    manualDownload,
    downloadPdf,
    state,
  })
</script>

<style scoped lang="css">
  .pdf-preview {
    position: relative;
    height: 100vh;
    padding: 20px 0;
    box-sizing: border-box;
    background-color: e9e9e9;
  }

  .pdf-preview .pdf-wrap {
    height: 100%;
    overflow-y: auto;
  }

  .vue-pdf-embed {
    height: 100%;
    text-align: center;
    width: 1000px;
    margin: 0 auto;
    box-sizing: border-box;
  }

  .page-tool {
    position: absolute;
    bottom: 35px;
    padding-left: 15px;
    padding-right: 15px;
    display: flex;
    align-items: center;
    background: rgb(66, 66, 66);
    color: white;
    border-radius: 19px;
    z-index: 100;
    cursor: pointer;
    margin-left: 50%;
    transform: translateX(-50%);
  }

  .page-tool-item {
    padding: 8px 15px;
    padding-left: 10px;
    cursor: pointer;
  }

  .loading,
  .no-file {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    font-size: 16px;
    color: #666;
    flex-direction: column;
  }

  .loading-content {
    text-align: center;
    width: 300px;
  }

  .loading-text {
    margin-bottom: 20px;
    font-size: 16px;
    color: #666;
  }

  .progress-bar {
    width: 100%;
    height: 8px;
    background-color: #f0f0f0;
    border-radius: 4px;
    overflow: hidden;
    margin-bottom: 10px;
  }

  .progress-fill {
    height: 100%;
    background: linear-gradient(90deg, #409eff 0%, #67c23a 100%);
    border-radius: 4px;
    transition: width 0.3s ease;
  }

  .progress-text {
    font-size: 14px;
    color: #409eff;
    font-weight: 500;
  }

  .no-file {
    color: #999;
    gap: 20px;
  }
</style>
